<?php
/**
 *MiniProgramServices
 * @author tan bing
 * @date 2021-08-11 9:36
 */


namespace Modules\User\Services\Api;


use EasyWeChat\Factory;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use EasyWeChat\MiniProgram\Application;
use Illuminate\Http\Request;
use Modules\Share\Entities\WechatApplet;
use Nwidart\Modules\Facades\Module;
use Tymon\JWTAuth\Facades\JWTAuth;
use Modules\User\Repositories\Contracts\UserInterface;
use Modules\User\Validate\Api\MiniProgramLoginValidate;

class MiniProgramServices extends BaseService
{

    /**
     * @var string[]
     * @author tan bing
     */
    protected array $config;

    /**
     * @var MiniProgramLoginValidate
     * @author tan bing
     */
    protected MiniProgramLoginValidate $validate;

    /**
     * @var UserInterface
     * @author tan bing
     */
    protected UserInterface $userInterface;

    /**
     * @var Request
     * @author tan bing
     */
    protected Request $request;

    /**
     * @var Application
     * @author tan bing
     */
    protected Application $app;

    /**
     * MiniProgramServices constructor.
     *
     * @param Request $request
     * @param UserInterface $userInterface
     * @author tan bing
     * @date 2021-08-11 11:55
     */
    public function __construct(Request $request, UserInterface $userInterface, MiniProgramLoginValidate $miniProgramLoginValidate)
    {
        if(Module::has('WechatApplet')) {
            $baseConfig = WechatApplet::query()->where('group', 'base')->get();
            foreach ($baseConfig as $item) {
                $item->code == 'app_id' && $appId = $item->value;
                $item->code == 'app_secret' && $appSecret = $item->value;
                $item->code == 'mch_id' && $mchId = $item->value;
                $item->code == 'mch_key' && $mchKey = $item->value;
                $item->code == 'cert_pem' && $certPem = $item->value;
                $item->code == 'key_pem' && $keyPem = $item->value;
            }
        }
        $this->config = [
            'app_id'     => $appId ?? '',
            'secret' => $appSecret ?? '',
            'mch_id'     => $mchId ?? '',
            'mch_key'    => $mchKey ?? '',
            'cert_pem'   => $certPem ?? '',
            'key_pem'    => $keyPem ?? '',
            'response_type' => 'array',
        ];
        try {
            $this->app = Factory::miniProgram($this->config);
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage());
        }
        $this->request = $request;
        $this->validate = $miniProgramLoginValidate;
        $this->userInterface = $userInterface;
    }

    /**
     * 小程序登录
     * @return array
     * @throws \Exception
     * @author tan bing
     * @date 2021-08-11 11:54
     */
    public function miniProgramLogin($data, $param)
    {
        $userInfo = !empty($param['user_info']) ? json_decode($param['user_info'], true) : [];
        if(!empty($param['share_code'])) {
            $parentUser = $this->userInterface->findById($param['parent']);
            if(!$parentUser) throw new \Exception('该分享码不存在或已失效');
            $userInfo['parent_id_1'] = $parentUser->id;
        }
        try {
            $userInfo['session_key'] = $data['session_key'];
            $userInfo['openid']      = $data['openid'];
            $exist = $this->userInterface->findByOpenid($userInfo['openid']);
            $scene = 'login';
            if(!$exist) {
                $scene = 'register';
                // 获取解密数据
                if(!empty($param['iv']) && !empty($param['encrypted_data'])) {
                    $info = $this->app->encryptor->decryptData($data['session_key'], $param['iv'], $param['encrypted_data']);
                    !empty($info['phoneNumber']) && $userInfo['mobile'] = $info['phoneNumber'];
                }
            }
            if(!$this->validate->scene($scene)->check($param)) {
//                throw new \Exception($this->validate->getError());
            }
            $user = $this->userInterface->registerUser($userInfo);
            $token = JWTAuth::fromUser($user);
            if (!$token) {
                throw new \Exception('登录失败');
            }
        } catch (\Exception $e) {

            throw new \Exception($e->getMessage());
        }

        return compact('user', 'token');
    }

    /**
     * 微信授权
     * @param string $type
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \Exception
     * @author tan bing
     * @date 2021-10-29 15:20
     */
    public function miniProgramSessionData($type = 'login')
    {
        $param = $this->request->input();
        if(!isset($param['code']) || empty($param['code'])) throw new \Exception('code不能为空');
        $code = $param['code'];
        try {
            $data = $this->app->auth->session($code);
            if (isset($data['errcode'])) {
                throw new \Exception('网络异常，请重新登录！');
            }
            if($type == 'login'){       // 登录
                $result = $this->miniProgramLogin($data, $param);
            }
            elseif($type == 'binging') {    // 绑定手机
                $result = $this->bindMobile($data, $param);
            } else {
                throw new \Exception('未知操作');
            }

        } catch (InvalidConfigException $e) {
            throw new \Exception($e->getMessage());
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage());
        }

        return $result;
    }

    /**
     * 绑定手机
     * @param $data
     * @param $param
     * @return mixed
     * @throws \Exception
     * @author tan bing
     * @date 2021-10-29 15:20
     */
    public function bindMobile($data, $param)
    {
        $userId = $this->request->attributes->get('userId');
        $user = $this->userInterface->findById($userId);
        // 获取解密数据
        try {
            $info = $this->app->encryptor->decryptData($data['session_key'], $param['iv'], $param['encryptedData']);
            if(empty($info['phoneNumber'])) throw new \Exception('授权获取手机号失败');
            $user->mobile = $info['phoneNumber'];
            if(!$user->save()) throw new \Exception('手机号绑定失败');
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage());
        }

        return $user;
    }
}